import { webSocket } from "@kit.NetworkKit"
import { BusinessError } from "@kit.BasicServicesKit"
import { consoleUtils } from "../../common/utils/consoleUtils"
import { __MESSAGE_TYPE_BUFFER_SIZE__, stringMessageDistributor } from "./StringMessageDistributor"
import { fileMessageDistributor } from "./FileMessageDistributor"
import { bufferUtils } from "../utils/bufferUtils"

/*
*1.前面6字节指定消息类型，string或者file
*2.file类型的话，消息类型后面20字节指定消息key，key后面20字节指定文件名称，文件名称后面6字节指定文件后缀
 */

export class WebSocketManager {
  private url: string = ''
  private client = webSocket.createWebSocket()
  private isClose = false

  private messageListeners: (Array<(value: string | ArrayBuffer) => void>) = []

  private eventListeners: (Map<string, Array<(err?: BusinessError, value?: string | ArrayBuffer) => void>>) = new Map()

  constructor(url: string) {
    this.url = url
    this.init()
    stringMessageDistributor.subscribeSend(async (message: string) => {
      await this.client.send(message)
    })
  }

  init() {
    this.client.connect(this.url).then(() => {
      console.log('WebSocketManager.ets -> init -> connect successfully')
      this.notify('connect')

      this.client.on('open', () => {
        console.log('WebSocketManager.ets -> init -> on open')
        this.notify('open')
      })

      this.client.on('message', (err: BusinessError, value: string | ArrayBuffer) => {
        console.log('WebSocketManager.ets -> init -> on message')
        this.notify('message', err, value)
        this.handleMessage(value)
      })

      this.client.on('error', (err) => {
        this.isClose = true
        consoleUtils.error('WebSocketManager.ets -> init -> on error: ', err)
        this.notify('error')
      })

      this.client.on('close', () => {
        this.isClose = true
        console.log('WebSocketManager.ets -> init -> on close')
        this.notify('close')
      })
    })
      .catch((err) => {
        this.isClose = true
        consoleUtils.error(`WebSocketManager.ets -> init -> connect error:`, err)
      })
  }

  handleMessage(value: string | ArrayBuffer) {
    if (value instanceof String) {
      stringMessageDistributor.distribute(value as string)
      return
    }
    let type: string = this.getMessageType(value)
    switch (type) {
      case 'string':
        stringMessageDistributor.distributeBuffer(value as ArrayBuffer)
      case 'file':
        fileMessageDistributor.distribute(value as ArrayBuffer)
    }
  }

  getMessageType(value: string | ArrayBuffer): string {
    if (value instanceof String) {
      return 'string'
    }
    let typeBuffer = value.slice(0, __MESSAGE_TYPE_BUFFER_SIZE__)
    return bufferUtils.bufferToStr(typeBuffer as ArrayBuffer).trim()
  }

  subscribe(event: string, listener: (err?: BusinessError, value?: string | ArrayBuffer) => void) {
    if (this.eventListeners.has(event)) {
      this.eventListeners.get(event)?.push(listener)
    } else {
      this.eventListeners.set(event, [listener])
    }
    return () => {
      let listeners = this.eventListeners.get(event)
      listeners ?.splice(listeners.findIndex(l => l === listener))
    }
  }

  notify(event, err?: BusinessError, value?: string | ArrayBuffer) {
    this.eventListeners.get(event)?.forEach(l => l(err, value))
  }

  async send(message: string) {
    if (this.isClose) {
      this.init()
      this.subscribe('connect', async () => {
        await this.send(message)
      })
    } else {
      await this.client.send(message)
    }
  }

  subscribeMessage(listener: (value: string | ArrayBuffer) => void) {
    this.messageListeners.push(listener)
    return () => {
      this.messageListeners.splice(this.messageListeners.findIndex(l => l === listener))
    }
  }

  notifyMessage(message: string) {
    this.messageListeners.forEach((l => l(message)))
  }

  close() {
    this.client.close()
  }
}